<?php
/* 
 * Copyright (c) 2018-2021   All rights reserved.
 * 
 * 创建时间：2021-08-27 09:28
 *
 * 项目：rm  -  $  - InstancePay.php
 *
 * 作者：liwei 
 */

namespace modules\levpays\sdk\wechatpay;

use Lev;
use lev\helpers\UserHelper;
use modules\levpays\BasePayIden;
use modules\levpays\helpers\levpaysSetHelper;
use modules\levpays\levpaysHelper;
use modules\levpays\table\tradesHelper;
use modules\levpays\table\UsersHelper;
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

!defined('INLEV') && exit('Access Denied LEV');

require_once __DIR__ . '/sdk/getallheaders.php';

class InstancePay
{

    public static $instance = null, $config = null;

    public static function loadConfig() {

        if (isset(static::$config)) return static::$config;

        static::$config = [
            'mchid'     => Lev::stget('mchid', 'levpays'),// 商户号，假定为`1000100`
            'mchcf'     => '',// API证书不重置，商户证书序列号就是个常量 - 通过证书加载
            'appid'     => Lev::stget('appid', 'levpays'),//应为公众号的APPID 示例值：wxd678efh567hg6787
            'appsecret' => Lev::stget('appsecret', 'levpays'), //appid 对应的 appsecret
            'certpem'   => Lev::uploadRootSrc(Lev::stget('certpem', 'levpays')), //平台证书
            'keypem'    => Lev::uploadRootSrc(Lev::stget('keypem', 'levpays')), //商户私钥
        ];
        empty(static::$config['mchid']) &&
        static::$config = is_file($file = __DIR__ . '/_lev.config.php') ? include $file : [];
        return static::$config;
    }

    public static function init() {
        if (isset(static::$instance)) return static::$instance;
        if (PHP_VERSION < '7.0.0') {
            Lev::$app['isAdmin'] && Lev::setNotices('抱歉，PHP7及以上才可以使用微信支付', true);
            return [];
        }
        static::loadConfig();
        if (empty(static::$config)) {
            static::$instance = [];
            Lev::$app['isAdmin'] && Lev::setNotices('抱歉，请在后台配置微信支付相关参数');
            return [];
        }

// 商户号，假定为`1000100`
        $merchantId = static::$config['mchid'];
// 商户私钥，文件路径假定为 `/path/to/merchant/apiclient_key.pem`
        $merchantPrivateKeyFilePath = static::$config['keypem'];
// 加载商户私钥
        $merchantPrivateKeyInstance = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);
        $merchantCertificateSerial = static::$config['mchcf'];// API证书不重置，商户证书序列号就是个常量

//        // 也可以使用openssl命令行获取证书序列号
//        // openssl x509 -in /path/to/merchant/apiclient_cert.pem -noout -serial | awk -F= '{print $2}'
//        // 或者从以下代码也可以直接加载
//        // 商户证书，文件路径假定为 `/path/to/merchant/apiclient_cert.pem`
//        $merchantCertificateFilePath = static::$config['certpem'];
//        // 加载商户证书
//        $merchantCertificateInstance = PemUtil::loadCertificate($merchantCertificateFilePath);
//        // 解析商户证书序列号
//        $merchantCertificateSerial = PemUtil::parseCertificateSerialNo($merchantCertificateInstance);


// 平台证书，可由下载器 `./bin/CertificateDownloader.php` 生成并假定保存为 `/path/to/wechatpay/cert.pem`
        $platformCertificateFilePath = static::$config['certpem'];
// 加载平台证书
        $platformCertificateInstance = PemUtil::loadCertificate($platformCertificateFilePath);
// 解析平台证书序列号
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateInstance);

// 工厂方法构造一个实例
        return static::$instance = Builder::factory([
            'mchid'      => $merchantId,
            'serial'     => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs'      => [
                $platformCertificateSerial => $platformCertificateInstance,
            ],
            // APIv2密钥(32字节)--不使用APIv2可选
            // 'secret' => 'exposed_your_key_here_have_risks',// 值为占位符，如需使用APIv2请替换为实际值
            // 'merchant' => [// --不使用APIv2可选
            //     // 商户证书 文件路径 --不使用APIv2可选
            //     'cert' => $merchantCertificateFilePath,
            //     // 商户API私钥 文件路径 --不使用APIv2可选
            //     'key' => $merchantPrivateKeyFilePath,
            // ],
        ]);
    }

    public static function paySign($prepay_id, $appid = '') {
        static::loadConfig();

        $merchantPrivateKeyFilePath = static::$config['keypem'];
        $merchantPrivateKeyInstance = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);

        $params = [
            'appId'     => $appid ?: static::$config['appid'],
            'timeStamp' => (string)Formatter::timestamp(),
            'nonceStr'  => Formatter::nonce(),
            'package'   => 'prepay_id='.$prepay_id,
        ];
        $params += ['paySign' => Rsa::sign(
            Formatter::joinedByLineFeed(...array_values($params)),
            $merchantPrivateKeyInstance
        ), 'signType' => 'RSA'];
        return $params;
    }

    public static function checkTradeNo($tradeNo) {
        $instance = static::init();
        if (!$instance) return Lev::responseMsg(-1, '未安装', ['_paySuccess'=>0]);
        $res = $instance->v3->pay->transactions->outTradeNo->{'{out_trade_no}'}
            ->getAsync([
                // 查询参数结构
                'query' => ['mchid' => static::$config['mchid']],
                // uri_template 字面量参数
                'out_trade_no' => $tradeNo,
            ])
            ->then(static function($response) {
                // 正常逻辑回调处理
                //echo $response->getBody(), PHP_EOL;
                $tradeInfo = json_decode($response->getBody(), true);
                return $tradeInfo;
            })
            ->otherwise(static function($e) {
                // 异常错误处理
                //echo $e->getMessage(), PHP_EOL;
                if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                    $r = $e->getResponse();
                    //echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
                    //echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
                    $tradeInfo = json_decode($r->getBody(), true);
                    return $tradeInfo;
                }
                //echo $e->getTraceAsString(), PHP_EOL;
            })
            ->wait();

        //print_r($res);exit;
        //if ($res['trade_state'] != 'SUCCESS') exit('支付失败');
        $res['_paySuccess'] = $res['trade_state'] == 'SUCCESS' || strpos($res['trade_state'], 'SUCCESS') !== false;//print_r($res);exit;
        return $res;
    }

    /**
     * 解密结果：Array
    (
        [mchid] => 1613292181
        [appid] => wxf68c61467c6b93ac
        [out_trade_no] => JSAPI--T0000000000000000003
        [transaction_id] => 4200001207202108295154216525
        [trade_type] => JSAPI
        [trade_state] => SUCCESS
        [trade_state_desc] => 支付成功
        [bank_type] => OTHERS
        [attach] =>
        [success_time] => 2021-08-29T02:14:02+08:00
        [payer] => Array
        (
            [openid] => oHk8F6D7KliPv9d2BN-WBZcfWLjc
        )

        [amount] => Array
        (
            [total] => 2
            [payer_total] => 2
            [currency] => CNY
            [payer_currency] => CNY
        )

    )
     * @param $json
     * @return mixed
     */
    public static function decrypt($json) {

        $arr = json_decode($json, true);
        $config = InstancePay::loadConfig();//print_r($str);exit;
        $res = AesGcm::decrypt($arr['resource']['ciphertext'], $config['APIv3secret'], $arr['resource']['nonce'], $arr['resource']['associated_data']);
        return json_decode($res, true);
    }

    public static function notify() {
//        $string = file_get_contents("php://input");
//        $string && $payInfo = static::decrypt($string);
//        if (!empty($payInfo)) {
//            $tradeId = tradesHelper::tradeNoToId($payInfo['out_trade_no']);
//            $ck = tradesHelper::findOne(['id'=>$tradeId]);
//            if ($ck && $ck['tradestatus'] == 4) {
//
//            }
//        }
        return json_encode(['code'=>'SUCCESS', 'message'=>'成功'], JSON_UNESCAPED_UNICODE);
    }

    public static function notifyUrl() {
        return Lev::toRoute(['/levpays_wx_notify.php']);
    }

    public static function getOutTradeNo($tradeNo, $pre) {
        return levpaysHelper::getOutTradeNo($tradeNo, $pre);
    }

    public static function isWxmini() {
        return Lev::ckUserAgent('miniProgram');
    }

    public static function paySuccess($res, $tradeNo, $pre, $outTradeInfo = [])
    {
        if (strpos($res['message'], '单号重复') !== false) {//$res['code'] == 'INVALID_REQUEST'
            $tradeId = tradesHelper::tradeNoToId($tradeNo);
            tradesHelper::update(['tradestatus' => 5], ['id'=>$tradeId]);
        }else if (strpos($res['message'], '已支付') !== false) {//$res['code'] == 'ORDERPAID'
            static::loadConfig();

            $tradeId = tradesHelper::tradeNoToId($tradeNo);
            $tradeInfo = tradesHelper::tradeInfo($tradeId);
            if ($tradeInfo && (!$tradeInfo['tradestatus'] || isset($res['_admin_check']))) {
                if (!$tradeInfo['uid']) {
                    empty($outTradeInfo) && $outTradeInfo = static::checkTradeNo(static::getOutTradeNo($tradeNo, $pre));
                }
                $outTradeInfo['openid'] = $outTradeInfo['payer']['openid'];
                static::$config['openidField'] = 'wxopenid';
                levpaysHelper::paySuccess($res, $tradeNo, $pre, $outTradeInfo, static::$config);
            }
        }
    }

}











